﻿#pragma warning(disable: 4996)

#include <time.h>
#include <stdarg.h>
#include <cassert>
#include "time/gettimeofday.h"

#include "ini/SimpleIni.h"
#include "flogger.h"
#ifdef WIN32
	#include <io.h>
	#include <direct.h>
  #include<sys/timeb.h>
  #define vsnprintf _vsnprintf
#else
	#include <sys/stat.h>
	#include <sys/types.h>
	#include <unistd.h>
	#include <dirent.h>
	#include <limits.h>
#endif 

#include "utf8code/UTF8Code.h"
#include "loggerdefine.h"

//#include <stdio.h>
//#include <iostream>
#ifdef USE_LOGGER_LOG4CPP
#pragma execution_character_set("utf-8")
#include "log4cpp/Category.hh"
#include "log4cpp/Appender.hh"
#include "log4cpp/OstreamAppender.hh"
#include "log4cpp/RollingFileAppender.hh"
#include "log4cpp/PropertyConfigurator.hh"
#include "log4cpp/Layout.hh"
#include "log4cpp/BasicLayout.hh"
#include "log4cpp/PatternLayout.hh"
#include "log4cpp/Priority.hh"
#include "log4cpp/NDC.hh"

#ifdef WIN32
#include "log4cpp/Win32DebugAppender.hh"
#endif
#endif /* #ifdef USE_LOGGER_LOG4CPP */

#include "get_thread_id.h"


#if defined(__mips__) || defined (__arm__) 
#define RUNNING_ON_EMBED_DEVICE 1
#endif

#if defined(__APPLE__) && defined (WRITE_SYS_LOG) 
#include "phlogger_apple.h"
#include <fcntl.h>
#include <asl.h>
#include <unistd.h>
#include <syslog.h>

AppleLogger AppleLogger::s_logger;

AppleLogger::~AppleLogger()
{
    //if (log_client) asl_close(log_client);
    closelog();
}
void AppleLogger::open( const char *name, logstyle style )
{
    //log_client = asl_open(name, name, ASL_OPT_STDERR);
    openlog(name, (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON);
    setlogmask(0xFFFFFFFF);

    m_name = name;
    
}

void AppleLogger::write_log(int evt,const char* str,...)
{
    if(!(evt & m_flag)) return ;
    ///*
    // #define	LOG_EMERG	0	/* system is unusable */
    //#define	LOG_ALERT	1	/* action must be taken immediately */
    //#define	LOG_CRIT	2	/* critical conditions */
    //#define	LOG_ERR		3	/* error conditions */
    //#define	LOG_WARNING	4	/* warning conditions */
    //#define	LOG_NOTICE	5	/* normal but significant condition */
    //#define	LOG_INFO	6	/* informational */
    //#define	LOG_DEBUG	7	/* debug-level messages */
    // */
//    enum LOG_EVENT
//    {
//        //! 消息
//        LOG_INFO	= 1,
//        //! 警告
//        LOG_WARNING = 2,
//        //! 错误
//        LOG_ERROR	= 4,
//        //! 调试
//        LOG_DEBUG	= 8
//    };
    int nLevel;
    switch (evt) {
        case 1:
            nLevel = 1;//LOG_ALERT;
            break;
        case 2:
            nLevel = 2;//LOG_CRIT;
            break;
        case 4:
            nLevel = 3;//LOG_ERR;
            break;
        case 8:
            nLevel = 7;//LOG_DEBUG
            break;
        default:
            nLevel = 0;
            break;
    }
    char *fmtstr = NULL;
    int len = 0;
    va_list   varg;
    va_start(varg,   str);
    len = _vscprintf( str, varg );
    fmtstr = new char[len+1];
    fmtstr[len] = 0;
    vsnprintf(fmtstr,len, str,   varg);
    va_end(varg);

    syslog(nLevel, fmtstr);
    delete [] fmtstr;
    //va_end(varg);
    //int asl_log(aslclient asl, aslmsg msg, int level, const char *format, ...) __printflike(4, 5);
}

#endif

#ifndef _vscprintf
#define _vscprintf(a,b) 10240
#endif
//#define MAX_LOGGER_BUFFER



flogger flogger::s_logger;

#if !defined(_WIN32) && !defined(__IOS__)
// delete earliest log file
static void delete_earliest_logfile(const char* pszDirectory, const char* pszFilename, unsigned int max_file_num)
{
	int  nFileNum = 0; // file number
	time_t tmModifyTime = time(NULL); 
	std::string strLogFile = "";

	DIR  * pdir ;
	struct dirent * pdirent;
	struct stat f_ftime;

	char full_path[512] = {0};

	pdir = opendir(pszDirectory);
	if( pdir == NULL )
	{
		return;
	}

	for(pdirent = readdir(pdir);pdirent != NULL;pdirent = readdir(pdir))
	{
		if( strcmp(pdirent->d_name, ".")==0 || strcmp(pdirent->d_name, "..")==0 ) 
		{
			continue;
		}

		if( strstr(pdirent->d_name, pszFilename) == 0 )
		{
			continue;
		}

		memset(full_path, 0, sizeof(full_path));
		snprintf(full_path, sizeof(full_path), "%s/%s", pszDirectory, pdirent->d_name);

		if( stat(full_path, &f_ftime) != 0 )
		{
			if( S_ISDIR(f_ftime.st_mode) )
			{
				continue;
			}
		}
		if( f_ftime.st_mode & S_IFDIR )
		{
			continue;
		}

		if( tmModifyTime >= f_ftime.st_mtime )
		{
			tmModifyTime = f_ftime.st_mtime;
			strLogFile = full_path;
		}

		nFileNum++;
	}

	// delete log file
	if( nFileNum >= max_file_num )
	{
		remove( strLogFile.c_str() );
	}

	closedir(pdir);	
}
#endif // POSIX


#ifdef WIN32
namespace
{
#pragma pack(push)
#pragma pack(1)

	struct LogRTTransferData
	{
		LogRTTransferData()
			: mVersion(1)
			, mLogLevel(0)
			, mLine(0)
			, mFileNameSize(0)
			, mFuncNameSize(0)
			, mMsgSize(0)
		{
			mSize = sizeof(LogRTTransferData);
			mMagicNumber = 0xF96335E0; // Use to identify message
			mBuff[0] = 0;
		}

		unsigned int mMagicNumber;
		unsigned int mSize;
		unsigned int mVersion;
		unsigned int mLogLevel;
		unsigned int mLine;
		unsigned int mFileNameSize; //end with NULL-terminator
		unsigned int mFuncNameSize; //end with NULL-terminator
		unsigned int mMsgSize; //end with NULL-terminator
		wchar_t mBuff[1]; //file name + message

		// ***Bytes not include NULL-terminator
		static char *create(unsigned int logLevel, unsigned int line, const char *filename, unsigned int filenameBytes,
			const char *funcname, unsigned int funcnameBytes
			, const char *msg, unsigned int msgBytes)
		{
			static LogRTTransferData __data_template;

			unsigned int dtSize = getSize(filenameBytes, funcnameBytes, msgBytes);
			LogRTTransferData *log = (LogRTTransferData *) new char[dtSize];
			memset((void *) log, 0, dtSize);
			log->mMagicNumber = __data_template.mMagicNumber;
			log->mSize = __data_template.mSize;
			log->mVersion = __data_template.mVersion;
			log->mLogLevel = logLevel;
			log->mLine = line;
			log->mFileNameSize = filenameBytes + sizeof(char);
			log->mFuncNameSize = funcnameBytes + sizeof(char);
			log->mMsgSize = msgBytes + sizeof(char);
			memcpy((void *) &log->mBuff, filename, filenameBytes);
			unsigned char * offset = (unsigned char *) &log->mBuff + log->mFileNameSize;
			memcpy((void *) offset, funcname, funcnameBytes);
			offset = (unsigned char *) offset + log->mFuncNameSize;
			memcpy((void *) offset, msg, msgBytes);

			return (char *) log;
		}

		// *** Bytes not include NULL-terminator!
		static unsigned int getSize(unsigned int filenameBytes, unsigned int funcnameBytes, unsigned int msgBytes)
		{
			return sizeof(LogRTTransferData) + filenameBytes + sizeof(wchar_t) + funcnameBytes + sizeof(wchar_t) + msgBytes + sizeof(wchar_t);
		}

		unsigned int size()
		{
			return sizeof(LogRTTransferData) + mFileNameSize + mFuncNameSize + mMsgSize;
		}

		void getFileName(std::wstring &fileName)
		{
			wchar_t *buff = mBuff;
			fileName = buff;
		}

		void getFuncName(std::wstring &funcName)
		{
			wchar_t *buff = (wchar_t *) ((unsigned char *) &mBuff + mFileNameSize);
			funcName = buff;
		}

		void getContent(std::wstring &content)
		{
			wchar_t *buff = (wchar_t *) ((unsigned char *) &mBuff + mFileNameSize + mFuncNameSize);
			content = buff;
		}
	};
#pragma pack(pop)
}
#endif


filelog::filelog(void)
:m_fstream(0)
,m_nflag(-1)
,m_nfsize(0)
,m_nbuf(0)
#ifdef RUNNING_ON_EMBED_DEVICE
,m_nmaxfsize(1024*64)
#else
,m_nmaxfsize(1024*1024*16)
#endif
,m_nmaxBuffsize(1024*1024)
,m_nOpenFailedCount(0)
#ifdef WIN32
,m_path(L".")
#else
,m_path(".")
#endif
,use_debug_file( false )
,m_bEnable(true)
,spliter_('\t')
,style_( with_fixedsize )
, m_nMaxFileNum(0)

#ifdef WIN32
,m_enable_rt_output(false)
,m_last_find_logwindow_time({0, 0})
,m_rt_logview_hwnd(nullptr)
#endif
{
	//loadcfg();
}

filelog::~filelog(void)
{
#ifndef USE_LOGGER_LOG4CPP
	close();
#if defined(__GLOBAL_NO_RELEASE__)
	exit(0);
#endif
#endif /* !!#ifndef USE_LOGGER_LOG4CPP */
}

bool filelog::open( const char*name, logstyle style/*=with_fixedsize*/, bool append/*false*/)
{
#ifdef USE_LOGGER_LOG4CPP
	/* 生成日志文件的存储路径 */
#ifdef WIN32
	_wmkdir(m_path.c_str());
#elif __APPLE__
	mkdir(m_path.c_str(), S_IRWXO | S_IRWXU);
#else
	std::string mkdir_path = "mkdir -p " + m_path;
	system(mkdir_path.c_str());
#endif

	m_name = name ? name : "";

	/*
	log4cpp支持两种设置模式:
	1、使用外部配置文件来配置Category、Appender、Layout
	2、使用程序配置Category、Appender、Layout
	*/
	log4cpp::Category& root = log4cpp::Category::getRoot();
	std::string strFileConf = name;
	strFileConf.append(".conf");
	if (!access(strFileConf.c_str(), 0/*F_OK*/))
	{
		log4cpp::PropertyConfigurator::configure(strFileConf.c_str());
		return true;
	}

	/* 程序配置Category、Appender、Layout */
	log4cpp::Appender* appender = NULL;
	if (m_nMaxFileNum > 1)
	{
		#ifdef WIN32
		USES_CONVERSION;
		std::string strFileName = W2A(m_path.c_str());
		strFileName.append("//");
		strFileName.append(name);
		strFileName.append(".log");
		#else
		std::string strFileName = m_path + "//" + name + ".log";
		#endif // WIN32

		appender = new log4cpp::RollingFileAppender("RollingFileAppender", strFileName, m_nmaxfsize);
	}
	else
	{
		#ifdef WIN32
		SYSTEMTIME st;
		GetLocalTime(&st);
		char szBuf[250] = {0};
		sprintf(szBuf, "%s.%d%02d%02d-%02d%02d%02d.log", name,
			st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
		USES_CONVERSION;
		std::string strFileName = W2A(m_path.c_str());
		strFileName.append("//");
		strFileName.append(szBuf);
		#else
		std::string strFileName = m_path + "//" + name + ".log";
		#endif // WIN32

		appender = new log4cpp::RollingFileAppender("RollingFileAppender", strFileName, m_nmaxfsize);
	}
	log4cpp::RollingFileAppender* prfAppender = (log4cpp::RollingFileAppender*)appender;
	prfAppender->setMaximumFileSize(m_nmaxfsize); // 设置单个循环日志的大小
	prfAppender->setMaxBackupIndex(m_nMaxFileNum); // 设置循环日志的数量

	log4cpp::Layout* layout = new log4cpp::PatternLayout();
	log4cpp::PatternLayout* pattLayout = (log4cpp::PatternLayout*)layout;
	pattLayout->setConversionPattern("%d{%Y-%m-%d %H:%M:%S.%l}  %p   %m%n");  // 日志格式：2019-12-30  15:09:33.835 ERROR xxxxxxxxxxxx

	appender->setLayout(layout);
	root.addAppender(appender);

#ifdef _WRITE_LOG_TO_CONSOLE
	// 如果支持输出到控制台，则添加控制台输出源
	log4cpp::Layout* layout1 = new log4cpp::PatternLayout();
	pattLayout = (log4cpp::PatternLayout*)layout1;
	pattLayout->setConversionPattern("%d{%Y-%m-%d %H:%M:%S.%l}  %p   %m%n");  // 日志格式：2019-12-30  15:09:33.835 ERROR xxxxxxxxxxxx

	log4cpp::Appender* appender1 = new log4cpp::OstreamAppender("Console", &std::cout);
	appender1->setLayout(pattLayout);

	root.addAppender(appender1);
#endif /* !!!#ifdef _WRITE_LOG_TO_CONSOLE */

	/* 设置日志的优先级 */
	switch (m_nflag)
	{
	case LOG_INFO://! 消息
		root.setPriority(log4cpp::Priority::INFO);
		break;

	case LOG_WARNING://! 警告
		root.setPriority(log4cpp::Priority::WARN);
		break;

	case LOG_ERROR2://! 错误
		root.setPriority(log4cpp::Priority::ERROR);
		break;

	case LOG_DEBUG://! 调试
		root.setPriority(log4cpp::Priority::DEBUG);
		break;
	}

	return true;
#else
	CAutoLock<CMutexLock> lock(m_fileLock);
	if(m_fstream) 
    return false;
  
#ifdef WIN32
	use_debug_file = !!PathFileExistsA( "C:\\sunlogin.debug" ) || !!PathFileExistsA( "C:\\sunlogin_debug" );
#endif

	m_nfsize = 0;

	//	m_path = path;
#ifdef WIN32
	_wmkdir( m_path.c_str());
#elif __APPLE__
  mkdir(m_path.c_str(),S_IRWXO|S_IRWXU);
#else
	std::string mkdir_path = "mkdir -p " + m_path;
	system(mkdir_path.c_str());
#endif
	
	style_ = style;
	m_name = name ? name : "";

  // Path
#ifdef WIN32
	std::wstring filename;
#else
	std::string filename;
#endif
	if (style_ == with_hour)
	{
		return true;
	}
	else
	{
#ifdef WIN32
		filename = CA2W(m_name.c_str());
#else
		filename = m_name;
#endif
	}

#ifdef WIN32
	std::wstring path = m_path + L"/" + filename + L".log";
#else
	std::string path = m_path +"/"+ filename +".log";
#endif

#ifdef WIN32
	if ( true )
#else
	if(0 == access(path.c_str(),0)) //文件已存在,重命名原来的文件
#endif
	{
#ifdef WRITE_LOG_WITH_APPEND
		m_fstream = fopen(path.c_str(), "a+b");
		if (m_fstream)
		{
			fseek(m_fstream, 0, SEEK_END);
			m_nfsize = ftell(m_fstream);
			if (m_nfsize >= m_nmaxfsize)
			{
				close();
				remove(path.c_str());
				m_nfsize = 0;
				m_fstream = fopen(path.c_str(), "a+b");
			}
		}
#elif defined RUNNING_ON_EMBED_DEVICE		
		//remove original file on embed devices, in stead of rename & loop
		remove(path.c_str());
		m_fstream = fopen(path.c_str(), "w+b");
#else
		
#ifdef WIN32
		wchar_t num[32] = L".log.1";
		std::wstring renamepath = m_path + L"/" + filename + num;
#else
		char num[32] = ".log.1";
		std::string renamepath = m_path + "/" + m_name + num;
#endif

		//保留最近100个日志 按时间戳命名
#ifdef WIN32
		SYSTEMTIME st;
		GetLocalTime(&st);
		swprintf(num, L".%d%02d%02d-%02d%02d%02d.log", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);

		renamepath = m_path + L"/" + filename + num;
		//文件名 xxx.20180828-204034.log

		//遍历文件夹删除同LOG下创建时间最久远的文件
		std::wstring file_name = m_path + L"/" + filename + L".*";

		WIN32_FIND_DATAW fd;
		FILETIME ftCreate, ftAccess, ftWrite;
		HANDLE hFile;
		HANDLE hFind = ::FindFirstFileW(file_name.c_str(), &fd);
		std::wstring del_filename = L"";
		int file_sum = 0;
		if (hFind != INVALID_HANDLE_VALUE)
		{
			WORD date, time;
			WORD date_temp = 0, time_temp = 0;
			do
			{
				std::wstring file_name = m_path + L"/" + fd.cFileName;
				++file_sum;
				hFile = CreateFileW(file_name.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
				GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite);
				FileTimeToDosDateTime(&ftCreate, &date, &time);
				if (date_temp == 0 && time_temp == 0)
				{
					date_temp = date;
					time_temp = time;
					del_filename = file_name.c_str();
				}
				else
				{
					if (date_temp == date)
					{
						if (time_temp > time)//后一个比前一个修改时间更早
							del_filename = file_name.c_str();
						time_temp = time;
					}
					else if (date_temp > date)
						del_filename = file_name.c_str();
					date_temp = date;
					time_temp = time;
				}
				CloseHandle(hFile);
			} while (::FindNextFileW(hFind, &fd));
			::FindClose(hFind);
		}
		//如果日志数量等于LOG_SUM 则删除最老的
		if (file_sum >= LOG_SUM)
			DeleteFileW(del_filename.c_str());
		m_fstream = _wfopen(renamepath.c_str(), L"w+b");
#elif !defined(__IOS__)
		if (append) {
			struct stat info;
			stat(path.c_str(), &info);
			m_nfsize = info.st_size;
			m_fstream = fopen(path.c_str(), "a+b");
			return (m_fstream != NULL);
		}
		if (m_nMaxFileNum > 1) 
		{
			// multiple log files are stored
			// new log file name
			char szSuffix[32] = {0};
			time_t now = time(NULL);
			struct tm tm;
			localtime_r(&(now), &(tm));
			(NULL != &tm) ? strftime(szSuffix, sizeof(szSuffix),"%Y%m%d-%H%M%S", &tm) : szSuffix[0] = '\0';
			renamepath = m_path + "/" + filename + "." + szSuffix + ".log"; // eg. file name is xxxx.20191016-165132.log

			// delete the earliest log file
			delete_earliest_logfile(m_path.c_str(), filename.c_str(), m_nMaxFileNum);

			// open log file
			rename(path.c_str(), renamepath.c_str());
			m_fstream = fopen(path.c_str(), "w+b");
		}
		else
		{
		int i = 1;
		for (i = 1; 0 == access(renamepath.c_str(),0) ; i++ )
		{
			sprintf(num, ".log.%d", i);
			renamepath = m_path + "/" + m_name + num;
		}
		rename(path.c_str(), renamepath.c_str());
		m_fstream = fopen(path.c_str(), "w+b");
		}
#endif	
#endif
	}
	else
	{
#ifdef WIN32
		m_fstream = _wfopen(path.c_str(), L"w+b");
#else
		m_fstream = fopen(path.c_str(),"w+b");
#endif
	}
	
	return m_fstream != NULL;
#endif /* #ifdef USE_LOGGER_LOG4CPP */
}

bool filelog::close()
{
#ifndef USE_LOGGER_LOG4CPP
#if !defined(__GLOBAL_NO_RELEASE__)
  CAutoLock<CMutexLock> lock(m_fileLock);
#endif
	if(m_fstream)
	{
		fflush(m_fstream);
		fclose(m_fstream);
		m_fstream = NULL;
	}
#endif /* !!#ifndef USE_LOGGER_LOG4CPP */
	return true;
}

#ifdef WIN32
bool filelog::write_log(int evt, const char *fileName, const char *funcName, int lineNum, const char* str, ...)
#else
bool filelog::write_log(int evt, const char* str, ...)
#endif
{
	if(!(evt & m_nflag)) 
    return true;
	if (!m_bEnable) 
    return true;
#ifdef USE_LOGGER_LOG4CPP
	log4cpp::Category& root = log4cpp::Category::getRoot();
	char *fmtstr = NULL;
	int len = 0;
	va_list varg;
	va_start(varg, str);
	len = _vscprintf(str, varg);
	fmtstr = new char[len + 1];
	fmtstr[len] = 0;
	vsnprintf(fmtstr, len, str, varg);
	va_end(varg);

	switch (evt)
	{
	case LOG_INFO://! 消息
		root.info(fmtstr);
		break;

	case LOG_WARNING://! 警告
		root.warn(fmtstr);
		break;

	case LOG_ERROR2://! 错误
		root.error(fmtstr);
		break;

	case LOG_DEBUG://! 调试
		root.debug(fmtstr);
		break;
	}

	delete[] fmtstr;
#else
	char st[32];
	char time_string[32];

	struct timeval us;
	memset(&us, '0', sizeof(struct timeval));
	time_t now = time(NULL);
	struct tm tm;
#ifdef WIN32
	localtime_s(&(tm), &(now));
#else
	localtime_r(&(now), &(tm));
#endif
	gettimeofday(&us, NULL);
	(NULL != &tm) ? strftime(time_string, sizeof(time_string),"%Y-%m-%d %H:%M:%S", &tm) : time_string[0] = '\0';
	sprintf(st, "%s.%03d%c", time_string, (int) (us.tv_usec/1000), spliter_ );

  char temp[16] = {0};
	const char* evtstr = string_event(evt);
	if(NULL == evtstr) 
  {
	  sprintf(temp,"x%08x",evt );
		evtstr = temp;
	}

	char *fmtstr = NULL;
	int len = 0;
	va_list varg;   
	va_start(varg, str);
	//len = vsnprintf(NULL, len, str, varg);
	len = _vscprintf( str, varg );
	fmtstr = new char[len+ 1];
	if( NULL == fmtstr ) 
  {
    printf("not enought memory!\n");
	va_end(varg);
    return false;
  }
  fmtstr[len] = 0;
	int actualLen = vsnprintf(fmtstr,len, str, varg);
	va_end(varg);


#ifdef WIN32
	if(m_enable_rt_output)
	{
		if(!m_rt_logview_hwnd)
		{
			timeval now = {0};
			gettimeofday(&now, nullptr);
			if(difftimeval(&m_last_find_logwindow_time, &now) >= 1000)
			{
				m_rt_logview_hwnd = ::FindWindowExW(HWND_MESSAGE, nullptr,
					L"OrayLogViewer-1129B972-BDE3-4D3F-8CD2-BC2D32B61C4C", L"OrayLogViewer-1129B972-BDE3-4D3F-8CD2-BC2D32B61C4C");
				m_last_find_logwindow_time = now;
			}
		}
		if(m_rt_logview_hwnd)
		{
			char *buff = LogRTTransferData::create(evt, lineNum,
				fileName, strlen(fileName) * sizeof(char),
				funcName, strlen(funcName) * sizeof(char),
				fmtstr, actualLen * sizeof(char));
			LogRTTransferData *log = (LogRTTransferData *) buff;

			COPYDATASTRUCT data = { 0 };
			data.dwData = 0;
			data.cbData = log->size();
			data.lpData = (void *) log;
			DWORD dwRet = SendMessage(m_rt_logview_hwnd, WM_COPYDATA, NULL, (LPARAM) &data);
			if(dwRet != 1)
			{
				m_rt_logview_hwnd = nullptr;
			}

			delete buff;
		}
	}
#endif


  // Log line 
	std::string line;
#if defined( WIN32 ) // && defined( _DEBUG )
  //if( use_debug_file ) {
    char processid[32] = {0};
    sprintf( processid, "[%u]\t", GetCurrentThreadId());
    line.append( processid, strlen( processid ) );
  //}
#else
    char processid[32] = {0};
    sprintf( processid, "[%u]\t", gettid2());
    line.append( processid, strlen( processid ) );
#endif
  line.append( st );
  line.append( evtstr, strlen( evtstr ) );
  line.append( 1, spliter_ );
	line.append( fmtstr );
	line.append( LOG_CRLF );

  if( style_ == with_hour )
  {
    write_file_with_hour( line );
  }
  else
  {
    // Save buf
    {
      CAutoLock<CMutexLock> lock(m_bufLock);	
	    if( ( m_buf[m_nbuf?0:1].size() + line.size() )	< m_nmaxBuffsize )
	    {
		    m_buf[m_nbuf?0:1].append(line);
	    }
    }
    
    write_file_with_fixedsize();

	}
	delete [] fmtstr;
#endif /* !!!#ifdef USE_LOGGER_LOG4CPP */

  return true;
}


bool filelog::write_log_console( int evt, const char* str, ... )
{
#ifdef USE_LOGGER_LOG4CPP
	log4cpp::Category& root = log4cpp::Category::getRoot();
	char *fmtstr = NULL;
	int len = 0;
	va_list varg;
	va_start(varg, str);
	len = _vscprintf(str, varg);
	fmtstr = new char[len + 1];
	if (NULL == fmtstr)
	{
		root.error("not enought memory!");
	}
	fmtstr[len] = 0;
	vsnprintf(fmtstr, len, str, varg);
	va_end(varg);

	switch (evt)
	{
	case LOG_INFO://! 消息
		root.info(fmtstr);
		break;

	case LOG_WARNING://! 警告
		root.warn(fmtstr);
		break;

	case LOG_ERROR2://! 错误
		root.error(fmtstr);
		break;

	case LOG_DEBUG://! 调试
		root.debug(fmtstr);
		break;
	}

	delete[] fmtstr;
#else
  if (!(evt & logConfigs)) 
  {
    return false;
  }

  char stime[32] = "";
  time_t now = time(NULL);
  struct tm *tm = localtime(&(now));
  if (tm)
  {
    strftime(stime, sizeof(stime), "%Y-%m-%d %H:%M:%S", tm);
  }
  char *fmtstr = NULL;
  int len = 0;
  va_list   varg;
  va_start(varg, str);
  len = _vscprintf(str, varg);
  fmtstr = new char[len + 1];
  fmtstr[len] = 0;
  vsnprintf(fmtstr, len, str, varg);
  va_end(varg);

  printf("%s%c0x%08x%c%s\n", stime, spliter_, evt, spliter_, fmtstr);
  delete[] fmtstr;
#endif /* !!!#ifdef USE_LOGGER_LOG4CPP */
  return true;
}

void filelog::enable( bool enable )
{
  m_bEnable = enable;
}

void filelog::Flag(unsigned long flag) 
{ 
	m_nflag = flag;

#ifdef USE_LOGGER_LOG4CPP
	/* 设置日志记录的优先级 */
	log4cpp::Category& root = log4cpp::Category::getRoot();
	switch (flag)
	{
	case LOG_INFO://! 消息
		root.setPriority(log4cpp::Priority::INFO);
		break;

	case LOG_WARNING://! 警告
		root.setPriority(log4cpp::Priority::WARN);
		break;

	case LOG_ERROR2://! 错误
		root.setPriority(log4cpp::Priority::ERROR);
		break;

	case LOG_DEBUG://! 调试(可以输出上面所有类型的日志)
	default:
		root.setPriority(log4cpp::Priority::DEBUG);
		break;
	}

#endif /* !!!#ifdef USE_LOGGER_LOG4CPP */
}

void filelog::Path(const char * path, unsigned int max_file_num/*=0*/) 
{ 
#ifdef WIN32
	std::string path_a = path;
	m_path = CUTF8_TO_W(path_a.c_str());
	m_nMaxFileNum = max_file_num;
#else
	m_path = path;
	m_nMaxFileNum = max_file_num;
	/*
	if (m_nMaxFileNum > 1) {
		char szCmd[256] = {0};
		sprintf(szCmd, "rm -f %s/*", path);
		system(szCmd);
	}
	*/
#endif
}

#ifdef WIN32
void filelog::Path(const wchar_t * path, int unsigned max_file_num/*=0*/)
{
	m_path = path;
	m_nMaxFileNum = max_file_num;
}
#endif

void filelog::MaxSize(unsigned long size) 
{ 
  m_nmaxfsize = size; 
#ifdef USE_LOGGER_LOG4CPP
  /* 设置日志文件大小 */
  log4cpp::Category& root = log4cpp::Category::getRoot();
  log4cpp::RollingFileAppender* appender = (log4cpp::RollingFileAppender*)root.getAppender("RollingFileAppender");
  assert(appender);
  if (appender)
  {
	  appender->setMaximumFileSize(size);
  }
#endif
}

void filelog::Split(const char spliter) 
{ 
  spliter_ = spliter; 
}

void filelog::SetFileMaxCount(unsigned short usFileMaxCount)
{
	m_nMaxFileNum = usFileMaxCount;

#ifdef USE_LOGGER_LOG4CPP
	/* 设置日志最大文件数量 */
	log4cpp::Category& root = log4cpp::Category::getRoot();
	log4cpp::RollingFileAppender* appender = (log4cpp::RollingFileAppender*)root.getAppender("RollingFileAppender");
	assert(appender);
	if (appender)
	{
		printf("Please set file_max_count before opening the log file");
	}
#endif
}

void filelog::SetEnableRealTimeOutput(bool enabled)
{
#ifdef WIN32
  m_enable_rt_output = enabled;
#endif
}

void filelog::debug_output( const char * str )
{
# ifdef WIN32
  OutputDebugStringA( str );
# endif
  printf("%s", str );
}

bool filelog::write_file_with_fixedsize()
{
#ifndef USE_LOGGER_LOG4CPP
  while( m_nOpenFailedCount < 2 )
	{
		CAutoLockEx<CMutexLock> lock(m_fileLock);
		if (!lock.Locked()) 
    {
      break;
    }
		{
			int index = 0; //当前用于写文件的buf
			{
				CAutoLock<CMutexLock> lock(m_bufLock);		
				if( m_buf[m_nbuf].empty() ) 
        {
          m_nbuf = m_nbuf?0:1;
        }
				index = m_nbuf;
			}
			if( m_buf[index].empty() ) 
        return true;
 
#ifdef DEBUG
      debug_output( m_buf[index].c_str() );
#endif
      //if(m_fstream == NULL) return false;
      if( m_fstream ) 
      {
			  if(0 == fwrite(m_buf[index].c_str(),m_buf[index].size(),1,m_fstream))
			  {
				  m_nOpenFailedCount ++;
				  close();
				  open( m_name.c_str(), style_ );
				  continue;
			  }
			  m_nOpenFailedCount = 0;
			  fflush(m_fstream);
			  m_nfsize += m_buf[index].size();
      } 
      else 
      {
#ifndef DISABLE_CONSOLE_PRINT
#ifndef DEBUG
        debug_output( m_buf[index].c_str() );
#endif 
#endif
      }

			m_buf[index].erase();
			if (m_nfsize >=m_nmaxfsize)
			{
				close();
				open(m_name.c_str());
			}
			CAutoLock<CMutexLock> lock(m_bufLock);
			m_nbuf = index?0:1;
		}
	}
#endif /* !!!#ifndef USE_LOGGER_LOG4CPP */
  return true;
}

void filelog::write_file_with_hour( const std::string& str )
{
#ifndef USE_LOGGER_LOG4CPP
  // Check file
  current_file_byhour();

  // Write file
  CAutoLock<CMutexLock> lock( m_fileLock );
  if( m_fstream && ( !str.empty() ) )
  {

    int retval = fwrite( ( const void* )str.c_str(), sizeof( char ), str.size(), m_fstream ); 
    if( retval <= 0 )
    {
      printf("[filelog] write failed(%d), %s:%s\n", errno, __FILE__, __FUNCTION__ );
    }
    else
    {
      fflush( m_fstream );
    }
  }
#endif /*!!! #ifndef USE_LOGGER_LOG4CPP */
}

/** \brief Get Current file by hour
*
*/

void filelog::current_file_byhour()
{
#ifndef USE_LOGGER_LOG4CPP
  char time_string[32] = {0};

#ifdef WIN32 
	time_t now = time(NULL);
	struct tm *tm = localtime(&now);
	struct timeb tb;
	ftime(&tb);
	strftime(time_string, sizeof(time_string),"%Y%m%d%H", tm );
#else
	struct timeval us;
	struct tm t;
	gettimeofday(&us,NULL);
	strftime(time_string, sizeof(time_string),"%Y%m%d%H", localtime_r(&us.tv_sec, &t));
#endif
 
	// File name
#ifdef WIN32
	std::wstring tTime = (const wchar_t *)CA2W(std::string(time_string, strlen(time_string)).c_str());
	std::wstring filename = (const wchar_t*)CA2W(m_name.c_str());
	std::wstring t_current_fname = (const wchar_t*)CA2W(current_fname_.c_str());
	std::wstring fname = m_path + L"/" + filename + L"." + tTime + L".log";
	// Switch log file 
	CAutoLock< CMutexLock > lock(m_fileLock);
	if (fname != t_current_fname)
	{
		if (m_fstream)
		{
			fclose(m_fstream);
		}

		current_fname_ = CW2A(fname.c_str());
		m_fstream = _wfopen(fname.c_str(), L"ab+");
	}
#else
	std::string fname = m_path + "/" + m_name + "." + std::string(time_string, strlen(time_string)) + ".log";
	// Switch log file 
	CAutoLock< CMutexLock > lock(m_fileLock);
	if (fname != current_fname_)
	{
		if (m_fstream)
		{
			fclose(m_fstream);
		}

		current_fname_ = fname;
		m_fstream = fopen(fname.c_str(), "ab+");
	}
#endif
#endif /*!!! #ifndef USE_LOGGER_LOG4CPP */  
}

flogger & flogger::GetInitance() 
{ 
  return s_logger; 
}


flogger::flogger()
{
	loadcfg();
}

void flogger::loadcfg(const char* file)
{
	std::string pathname;
	if(file)
	{
		pathname = file;
	}
	else
	{
#ifdef WIN32
		char path[MAX_PATH*3];
		GetModuleFileNameA(NULL,path,sizeof(path));
		pathname = path;
		size_t pos = pathname.find_last_of('\\');
		pathname = pathname.substr(0,pos);
		pathname += "/config.ini";
#else
	 pathname = "/etc/phremote.ini";
#endif
	}
	CSimpleIniA ini(true, true, true);

	if (ini.LoadFile(pathname.c_str()) < 0)
		return ;

	loadcfg(
		ini.GetValue("LOGGER", "PATH", ""),
		ini.GetLongValue("LOGGER","MASK", -1),
		ini.GetLongValue("LOGGER", "MAXSIZE", 16) * 1024 * 1024,
		(unsigned short)ini.GetLongValue("LOGGER", "FILE_MAX_COUNT", 0)
	);
}

void flogger::loadcfg(const char* lpstrPath, unsigned long nFlag, unsigned long nMaxSize, unsigned short usFileMaxCount/*=0*/)
{
	assert(lpstrPath);
	Flag(nFlag);
	Path(lpstrPath, nMaxSize);
	MaxSize(nMaxSize);
	SetFileMaxCount(usFileMaxCount);
}

